//
//  SQLiteManager.m
//  OCDemo
//
//  Created by zhumuzhi on 2021/5/12.
//

#import "SQLiteManager.h"
#import <sqlite3.h>

@interface SQLiteManager ()

@property (nonatomic, assign) sqlite3 *db;

@end

@implementation SQLiteManager

static id _instance;

#pragma mark - Share
+ (instancetype)shareInstance {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [[self alloc] init];
    });
    return _instance;
}

#pragma mark - 创建或者打开数据库
- (BOOL)openDB:(NSObject *)object
{
    // 获取沙盒路径,将数据库放入沙盒中
    NSString *filePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
    filePath = [filePath stringByAppendingPathComponent:@"ocDemo.sqlite"];
    NSLog(@"数据库路径：%@", filePath);
    // 如果有对应的数据库,则打开对应的数据库.如果没有对应的数据库,则创建数据库
    // 1> 参数一:文件路径(数据库的存放路径)
    // 2> 参数二:操作数据库的对象
    if (sqlite3_open(filePath.UTF8String, &_db) != SQLITE_OK) {
        NSLog(@"打开数据库失败");
        return NO;
    }
    NSLog(@"创建sqlite文件成功");
    if (object == nil) {
        NSLog(@"未传入对象或对象为空，未执行创表操作");
        return NO;
    }
    // 如果打开数据成功, 且传入了对象 则创建一张表,用于之前存放数据
    return [self createTable:object];
}

/// 创表功能，需要根据模型的属性自动添加，名称与类型
- (BOOL)createTable:(NSObject *)object
{
    // 1.获取对象名称
    NSString *tableName = [self getObjectTableName:object];
    NSString *propertyAndType = [self getAllProperties:object];
    // 2.定义创建表的SQL语句 (表名：t_ModelName )
    NSString *createTableSQL = [NSString stringWithFormat:@"CREATE TABLE IF NOT EXISTS %@ ('id' INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,%@);", tableName, propertyAndType];
    // 2.执行SQL语句
    return [self execSQL:createTableSQL];
}

#pragma mark - 执行SQL语句
- (BOOL)execSQL:(NSString *)sql
{
    // 执行sql语句
    // 1> 参数一:数据库sqlite3对象
    // 2> 参数二:执行的sql语句
    if (sqlite3_exec(self.db, sql.UTF8String, nil, nil, nil) == SQLITE_OK) {
        NSLog(@"执行SQL语句成功");
        return YES;
    }
    NSLog(@"执行SQL语句失败");
    return NO;
}

#pragma mark - 查询数据
- (NSArray *)querySQL:(NSString *)querySQL
{
    // 定义游标对象
    sqlite3_stmt *stmt = nil;
    
    // 准备工作(获取查询的游标对象)
    // 1> 参数三:查询语句的长度, -1自动计算
    // 2> 参数四:查询的游标对象地址
    if (sqlite3_prepare_v2(self.db, querySQL.UTF8String, -1, &stmt, nil) != SQLITE_OK) {
        NSLog(@"没有准备成功");
        return nil;
    }
    
    // 取出某一个行数的数据
    NSMutableArray *tempArray = [NSMutableArray array];
    // 获取字段的个数
    int count = sqlite3_column_count(stmt);
    while (sqlite3_step(stmt) == SQLITE_ROW) {
        NSMutableDictionary *dict = [NSMutableDictionary dictionary];
        for (int i = 0; i < count; i++) {
            // 1.取出当前字段的名称(key)
            NSString *key = [NSString stringWithUTF8String:sqlite3_column_name(stmt, i)];
            
            // 2.取出当前字段对应的值(value)
            const char *cValue = (const char *)sqlite3_column_text(stmt, i);
            NSString *value = [NSString stringWithUTF8String:cValue];
            
            // 3.将键值对放入字典中
            [dict setObject:value forKey:key];
        }
        
        [tempArray addObject:dict];
    }
    
    // 不再使用游标时,需要释放对象
    sqlite3_finalize(stmt);
    
    return tempArray;
}


#pragma mark - Private

// 获取模型类名
- (NSString *)getObjectTableName:(NSObject *)object
{
    NSString *className = NSStringFromClass([object class]);
    NSString *tableName = [NSString stringWithFormat:@"t_%@", className.lowercaseString];
    return tableName;
}

// 获取模型属性类型
- (NSString *)getObjectTableColumn:(NSObject *)object
{
    // 1.获取全部属性与属性类型
    // 2.转换拼接为 SQL 语句
    return [self getAllProperties:object];
}

/// 获取对象的全部属性
- (NSString *)getAllProperties:(NSObject *)object
{
    u_int count = 0;
    //传递count的地址
    // 1.获取全部属性与属性类型
    objc_property_t *properties = class_copyPropertyList([object class], &count);
    NSMutableArray *propertyArray = [NSMutableArray arrayWithCapacity:count];
    for (int i = 0; i < count; i++) {
        objc_property_t property = properties[i];
        //得到的propertyName为C语言的字符串
        const char *propertyName = property_getName(property);
        //得到的propertyAttributes
        const char *typeEncoding = property_copyAttributeValue(property, "T");
        NSString *typeEncodingStr = [NSString stringWithUTF8String:typeEncoding];
        typeEncodingStr = [typeEncodingStr stringByReplacingOccurrencesOfString:@"@" withString:@""];
        typeEncodingStr = [typeEncodingStr stringByReplacingOccurrencesOfString:@"\\" withString:@""];
        
        NSString *name = [NSString stringWithUTF8String:propertyName];
        NSString *type = [self parserType:typeEncodingStr];
        
        NSString *clounm = [NSString stringWithFormat:@"'%@' %@", name, type];
        [propertyArray addObject:clounm];
    }
    // 2.转换拼接为 SQL 语句
    NSString *tableClounmStr = [propertyArray componentsJoinedByString:@","];
//    NSLog(@"clounm-string：%@", tableClounmStr);
    free(properties);
    return tableClounmStr;
}

/// OC >> SQL 数据类型转化
- (NSString *)parserType:(NSString *)typeAbbr
{
    NSString *type;
    if([typeAbbr isEqualToString:@"\"NSString\""]) {
        typeAbbr = @"C";
    }
    const char charStr = *(typeAbbr.UTF8String);
    switch (charStr) {
        case 'B':
            type = SQLINTEGER;
//            NSLog(@"🔥Boolean类型-数据库使用 INTEGER");
            break;
        case 'c':
        case 'C':
            type = SQLTEXT;
//            NSLog(@"🔥字符串类型-数据库使用 TEXT");
            break;
        case 's':
        case 'S':
        case 'i':
        case 'I':
        case 'l':
        case 'L':
        case 'q':
        case 'Q':
            type = SQLINTEGER;
//            NSLog(@"🔥Int类型-数据库使用 INTEGER");
            break;
        case 'f':
            type = SQLREAL;
//            NSLog(@"🔥Float类型-数据库使用 REAL");
            break;
        case 'd':
        case 'D':
            type = SQLREAL;
//            NSLog(@"🔥Double类型-数据库使用 REAL");
            break;
        default:
            type = SQLNULL;
//            NSLog(@"🔥未检出数据");
            break;
    }
    return type;
}



@end
